﻿#region Copyright information
// 
// Copyright © 2005-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the Biological Object Library and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: TreeGenomeBase.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@gmail.com
// - Date Created: 2012-12-08 8:26 AM
// - Last Modified: 2013-01-25 3:59 PM
// 
#endregion
using System;
using BOL.Algorithms.Tree;

namespace BOL.Algorithms.Optimization.EvolutionaryAlgorithms
{
    public abstract class TreeGenomeBase<TGene> : GenomeBase<TGene>, ITreeGenome<TGene>
    {
        #region Public properties

        public ITreeNode<TGene> RootNode { get; set; }

        public int Count
        {
            get
            {
                var count = 0;
                CountNode(RootNode, ref count);

                return count;
            }
        }

        #endregion

        #region Constructors

        protected TreeGenomeBase(Random r, ITreeNode<TGene> rootNode)
            : base(r)
        {
            RootNode = rootNode;
        }

        #endregion

        #region Public methods

        private static void CountNode(ITreeNode<TGene> treeNode, ref int count)
        {
            foreach (var child in treeNode.Children)
            {
                count++;
                CountNode(child, ref count);
            }
        }

        private static ITreeNode<TGene> NodeAt(ITreeNode<TGene> treeNode, int position, ref int current)
        {
            foreach (var child in treeNode.Children)
            {
                current++;
                var node = NodeAt(child, position, ref current);
                if (position == current)
                    return node;
            }

            return treeNode;
        }

        private void Crossover(ITreeGenome<TGene> other)
        {
            var dadPosition = Random.Next(Count);
            var momPosition = Random.Next(other.Count);

            var current = 0;
            var dadNode = NodeAt(RootNode, dadPosition, ref current);
            current = 0;
            var momNode = NodeAt(other.RootNode, momPosition, ref current);

            var dadNodeParent = dadNode.Parent;
            var momNodeParent = momNode.Parent;
            dadNode.RemoveFromParent();
            momNode.RemoveFromParent();
            dadNodeParent.Add(momNode);
            momNodeParent.Add(dadNode);
        }

        public override void Crossover(IGenome<TGene> other)
        {
            if (other is ITreeGenome<TGene>)
                Crossover(other as ITreeGenome<TGene>);
            else
                throw new InvalidCastException("Other is not an ITreeGenome<TGene> type.");
        }

        public override void Mutate(double mutationRate)
        {
            var numbersToBeMutated = (int)Math.Round(Count * mutationRate);
            for (var i = 0; i < numbersToBeMutated; i++)
            {
                var sample = Random.NextDouble();
                if (sample < 0.34)
                    DestructSubtree();
                else if (sample >= 0.67)
                    SwapSubtree();
                else
                    SwapNode();
            }
        }

        /// <summary>
        /// <see>http://lancet.mit.edu/~mbwall/presentations/IntroToGAs/P004.html</see>
        /// </summary>
        private void DestructSubtree()
        {
            throw new NotImplementedException();
        }

        private void SwapNode()
        {
            throw new NotImplementedException();
        }

        private void SwapSubtree()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IEquatable<ITreeGenome<TGene>> implementation

        public bool Equals(ITreeGenome<TGene> other)
        {
            return RootNode.Equals(other.RootNode);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            return RootNode.GetHashCode();
        }

        public override bool Equals(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is ITreeGenome<TGene>))
                throw new InvalidCastException("The 'other' argument is not an ITreeGenome<TGene> object.");

            return Equals(other as ITreeGenome<TGene>);
        }

        public override string ToString()
        {
            return RootNode.ToString();
        }

        #endregion
    }
}
